<template>
  <div class="fx-date">
    <div class="fx-date-header">
      <span @click.stop="dateYearClick(false)">
        <i class="iconfont icon-double-left"/>
      </span>
      <span @click.stop="dateMonthClick(false)" v-show="pickerTemplate==='day'">
        <i class="iconfont icon-left"/>
      </span>
      <div class="fx-date-time">
        <div class="year" @click.stop="pickerTemplate='year'">
          {{ yearText }}
        </div>
        <div class="month" v-show="pickerTemplate!=='year'&&pickerTemplate!=='month'"
             @click.stop="pickerTemplate='month'">
          {{ dateInfo.month + 1 }}月
        </div>
      </div>
      <span @click.stop="dateMonthClick(true)" v-show="pickerTemplate==='day'">
        <i class="iconfont icon-right"/>
      </span>
      <span @click.stop="dateYearClick(true)">
        <i class="iconfont icon-double-right"/>
      </span>
    </div>
    <template v-if="pickerTemplate==='day'&&(dateType==='day'||dateType==='week')">
      <ul class="fx-date-main" @mouseleave.stop="weekSelectShow=false">
        <li v-for="item in weekTitle" :key="item" class="title" @mouseenter.stop="weekSelectShow=false">{{ item }}</li>
        <li v-for="(item,index) in dateList" :key="item.id" class="fx-date-day"
            @click.stop="datePickerDayClick(item)" @mousemove.stop="datePickerMousemove($event,index)"
            :class="{current:item.isCurrentMonth,
                    today:currentDate.format===item.formatDate,
                    active:item.formatDate===activeDate.format}">
          <span>{{ item.day }}</span>
        </li>
        <li class="fx-date-week" :style="weekSelectStyle" v-if="dateType==='week'&&weekSelectShow"></li>
      </ul>
    </template>
    <template v-if="pickerTemplate==='month'">
      <ul class="fx-date-main-month">
        <li v-for="item in monthList" :key="item"
            :class="{today:item.month===currentDate.month&&item.year===currentDate.year,
                     active:item.month===activeDate.month&&item.year===activeDate.year}"
            @click.stop="monthListClick(item)">
          <span>{{ item.monthText }}</span>
        </li>
      </ul>
    </template>
    <template v-if="pickerTemplate==='year'">
      <ul class="fx-date-main-year">
        <li v-for="item in yearList" :key="item"
            :class="{today:Number(item)===currentDate.year,active:Number(item)===activeDate.year}"
            @click.stop="yearListClick(item)">
          <span>{{ item }}</span>
        </li>
      </ul>
    </template>
    <div class="fx-date-tip"></div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, onBeforeMount, PropType, reactive, ref, SetupContext } from 'vue'
import { dateFormat, getMonthDay, randomId } from '@/base/utils'

type DateType = {
  id: string;
  year: number;
  month: number;
  day: number;
  isCurrentMonth: boolean;
  formatDate: string;
}

type MonthListType = {
  id: string;
  year: number;
  month: number;
  monthText: string;
}

interface DateProps {
  date: string;
  dateType: 'day' | 'week' | 'month' | 'year';
}

export default defineComponent({
  name: 'fxDate',
  props: {
    date: {
      type: String as PropType<string>,
      default: ''
    },
    dateType: {
      type: String as PropType<'day' | 'week' | 'month' | 'year'>,
      default: 'day'
    }
  },
  emits: ['update:date', 'close'],
  setup (props: DateProps, ctx: SetupContext) {
    // 根据当前选择模板渲染
    const pickerTemplate = ref<'day' | 'month' | 'year'>('day')
    const dateList = ref<DateType[]>([])
    const weekTitle = ref<string[]>(['日', '一', '二', '三', '四', '五', '六'])
    const weekSelectTop = ref<number>(0)
    const weekSelectShow = ref<boolean>(false)
    // 今天当前时间
    const currentDate = reactive<{ day: number; month: number; year: number; format: string }>({
      day: new Date().getDate(),
      month: new Date().getMonth(),
      year: new Date().getFullYear(),
      format: dateFormat('YYYY-MM-DD')
    })
    // 当前选中时间
    const activeDate = computed<{ day: number; month: number; year: number; format: string }>(() => {
      return {
        day: new Date(props.date).getDate(),
        month: new Date(props.date).getMonth(),
        year: new Date(props.date).getFullYear(),
        format: dateFormat('YYYY-MM-DD', new Date(props.date))
      }
    })
    // 渲染时日期
    const dateInfo = reactive({
      year: new Date(props.date).getFullYear(),
      month: new Date(props.date).getMonth(),
      day: new Date(props.date).getDate()
    })
    // 日期-年
    const yearList = computed<string[]>(() => {
      const year = String(dateInfo.year).substring(0, 3)
      const list: string[] = []
      for (let i = 0; i < 10; i++) {
        list.push(`${year}${i}`)
      }
      return list
    })
    // 日期-月
    const monthList = computed<MonthListType[]>(() => {
      return Array.from(new Array(12)).map((item: undefined, index: number) => {
        const month = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
        return {
          id: randomId(),
          year: dateInfo.year,
          month: index,
          monthText: month[index]
        }
      })
    })
    // 日期 年位置显示文字
    const yearText = computed(() => {
      if (pickerTemplate.value === 'year') {
        const year = String(dateInfo.year).substring(0, 3)
        return `${year}0年 - ${year}9年`
      } else {
        return `${dateInfo.year}年`
      }
    })
    // 日期-周 样式
    const weekSelectStyle = computed(() => {
      if (props.dateType !== 'week') return
      return {
        top: `${weekSelectTop.value}px`
      }
    })

    const initDateList = (date: Date = new Date()) => {
      dateList.value = []
      date = new Date(date)
      // 当前月最后一天 当前月一共天数
      const monthLastDay = getMonthDay()
      // 上个月最后一天 上个月一共天数
      const beforeMonthLastDay = getMonthDay(date.getFullYear(), date.getMonth() - 1)
      // 当前月第一天星期数
      const firstDayWeek = new Date(date.getFullYear(), date.getMonth(), 1).getDay()
      // 获取日历当前月第一天日期
      const firstDatePicker = firstDayWeek === 0 ? firstDayWeek : beforeMonthLastDay - firstDayWeek + 1
      Array.from(new Array(6 * 7)).forEach((item: undefined, index: number) => {
        const beforeMonth = firstDatePicker + index
        const mouthDay = index - firstDayWeek + 1
        const month = date.getMonth() - (beforeMonth <= beforeMonthLastDay ? 1 : mouthDay <= monthLastDay ? 0 : -1)
        const day = beforeMonth <= beforeMonthLastDay ? beforeMonth : mouthDay <= monthLastDay ? mouthDay : mouthDay - monthLastDay
        const time = new Date(date.getFullYear(), month, day)
        dateList.value.push({
          id: randomId(),
          year: Number(dateFormat('YYYY', time)),
          month: Number(dateFormat('MM', time)),
          day: day,
          isCurrentMonth: beforeMonth > beforeMonthLastDay && mouthDay <= monthLastDay,
          formatDate: dateFormat('YYYY-MM-DD', time)
        })
      })
    }

    const dateYearClick = (isAdd: boolean): void => {
      if (pickerTemplate.value === 'year') {
        isAdd ? dateInfo.year += 10 : dateInfo.year -= 10
        return
      }
      isAdd ? dateInfo.year++ : dateInfo.year--
      initDateList(new Date(dateInfo.year, dateInfo.month))
    }

    const dateMonthClick = (isAdd: boolean): void => {
      isAdd ? dateInfo.month++ : dateInfo.month--
      if (dateInfo.month >= 12) {
        dateInfo.year++
        dateInfo.month = 0
      }
      if (dateInfo.month < 0) {
        dateInfo.year--
        dateInfo.month = 11
      }
      initDateList(new Date(dateInfo.year, dateInfo.month))
    }

    const datePickerDayClick = (data: DateType) => {
      if (props.dateType === 'week') return
      if (!data.isCurrentMonth) {
        initDateList(new Date(data.year, data.month - 1, data.day))
        dateInfo.year = data.year
        dateInfo.month = data.month - 1
      }
      ctx.emit('update:date', data.formatDate)
      ctx.emit('close')
    }

    const monthListClick = (data: MonthListType) => {
      dateInfo.month = data.month
      if (props.dateType === 'month') {
        ctx.emit('update:date', dateFormat('YYYY-MM', new Date(data.year, data.month)))
        ctx.emit('close')
        return
      }
      pickerTemplate.value = 'day'
      initDateList(new Date(dateInfo.year, dateInfo.month))
    }

    const yearListClick = (year: string) => {
      dateInfo.year = Number(year)
      if (props.dateType === 'year') {
        ctx.emit('update:date', String(year))
        ctx.emit('close')
        return
      }
      pickerTemplate.value = props.dateType === 'month' ? 'month' : 'day'
      initDateList(new Date(dateInfo.year, dateInfo.month))
    }

    const datePickerMousemove = (ev: MouseEvent, index: number) => {
      if (props.dateType !== 'week') return
      weekSelectShow.value = true
      weekSelectTop.value = Math.floor(index / 7) * 38 + 40 + 38 + 20 + 6
    }

    onBeforeMount(() => {
      pickerTemplate.value = props.dateType !== 'week' ? props.dateType : 'day'
      initDateList(new Date(props.date))
    })

    return {
      dateList,
      yearList,
      monthList,
      weekTitle,
      yearText,
      dateInfo,
      currentDate,
      activeDate,
      weekSelectStyle,
      weekSelectShow,
      pickerTemplate,
      dateYearClick,
      dateMonthClick,
      datePickerDayClick,
      yearListClick,
      monthListClick,
      datePickerMousemove
    }
  }
})
</script>
